/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-02-19     tinnu       the first version
 */
#include <tarm_iic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static I2C_StatusType I2C_WaitOnFlagUntilTimeout(I2C_Type* I2Cx, uint32_t Flag, FlagStatus Status, uint32_t EventCheck, uint32_t Timeout);
void I2C_ClearADDRFlag(I2C_Type* I2Cx);
void I2C_ClearSTOPFlag(I2C_Type* I2Cx);
void I2C_Delay(uint32_t us);

void I2C1_Init(void)
{
    I2C_InitType  I2C_InitStructure;

    LowLevel_Init();

    /* I2C configuration */
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2CDevice;
    I2C_InitStructure.I2C_FmDutyCycle = I2C_FmDutyCycle_2_1;
    I2C_InitStructure.I2C_OwnAddr1 = I2C_SLAVE_ADDRESS7;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AddrMode = I2C_AddrMode_7bit;
    I2C_InitStructure.I2C_BitRate = I2C_SPEED;

    /* I2C Peripheral Enable */
    I2C_Cmd(I2C_PORT, ENABLE);

    /* Apply I2C configuration after enabling it */
    I2C_Init(I2C_PORT, &I2C_InitStructure);
}

void I2C_Test(){

    I2C_InitType  I2C_InitStructure;

    LowLevel_Init();

    /* I2C configuration */
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2CDevice;
    I2C_InitStructure.I2C_FmDutyCycle = I2C_FmDutyCycle_2_1;
    I2C_InitStructure.I2C_OwnAddr1 = I2C_SLAVE_ADDRESS7;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AddrMode = I2C_AddrMode_7bit;
    I2C_InitStructure.I2C_BitRate = I2C_SPEED;

    /* I2C Peripheral Enable */
    I2C_Cmd(I2C_PORT, ENABLE);

    /* Apply I2C configuration after enabling it */
    I2C_Init(I2C_PORT, &I2C_InitStructure);

    uint8_t tx_buf[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
    uint8_t addr_buf[] = {0x74};

    I2C_Master_Transmit(I2C_PORT, I2C_SLAVE_ADDRESS7, addr_buf, 1, 1000);
    I2C_Master_Rec(I2C_PORT, I2C_SLAVE_ADDRESS7, tx_buf, 4, 1000);

    addr_buf[0] = 0x75;
    I2C_Master_Transmit(I2C_PORT, I2C_SLAVE_ADDRESS7, addr_buf, 1, 1000);
    I2C_Master_Rec(I2C_PORT, I2C_SLAVE_ADDRESS7, tx_buf+4, 4, 1000);

}

void LowLevel_Init(void)
{
  GPIO_InitType GPIO_InitStructure;

  /* I2C_SCL_GPIO_CLK and I2C_SDA_GPIO_CLK Periph clock enable */
  RCC_APB2PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK | RCC_APB2PERIPH_AFIO , ENABLE);

  /* I2C Periph clock enable */
  RCC_APB1PeriphClockCmd(I2C_CLK, ENABLE);
  GPIO_PinsRemapConfig(GPIO_Remap_I2C1,ENABLE);

  /* GPIO configuration */
  /* Configure I2C pins: SCL */
  GPIO_InitStructure.GPIO_Pins = I2C_SCL_PIN;
  GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

  /* Configure I2C pins: SDA */
  GPIO_InitStructure.GPIO_Pins = I2C_SDA_PIN;
  GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
}

static I2C_StatusType I2C_WaitOnFlagUntilTimeout(I2C_Type* I2Cx, uint32_t Flag, FlagStatus Status, uint32_t EventCheck, uint32_t Timeout)
{
  /* delay 10 us = ms * 100 */
  Timeout *= 33;

  while(I2C_GetFlagStatus(I2Cx, Flag) == Status)
  {
    /* Check ACKFAIL Flag */
    if(EventCheck & I2C_EVT_CHECK_ACKFAIL)
    {
      if(I2C_GetFlagStatus(I2Cx, I2C_FLAG_ACKFAIL) == SET)
      {
        /* Send STOP Condition */
        I2C_GenerateSTOP(I2Cx, ENABLE);

        /* Clear AF Flag */
        I2C_ClearFlag(I2Cx, I2C_FLAG_ACKFAIL);

        return I2C_ERROR;
      }
    }

    /* Check STOP Flag */
    if(EventCheck & I2C_EVT_CHECK_STOP)
    {
      if(I2C_GetFlagStatus(I2Cx, I2C_FLAG_STOPF) == SET)
      {
        /* Clear STOP Flag */
        I2C_ClearSTOPFlag(I2Cx);

        return I2C_ERROR;
      }
    }

    /* 10 us delay */
//    I2C_Delay(10);
    delay_ms(1);

    /* Check for the Timeout */
    if((Timeout--) == 0)
    {
      return I2C_TIMEOUT;
    }
  }

  return I2C_OK;
}

void I2C_ClearADDRFlag(I2C_Type* I2Cx)
{
  __IO uint32_t tmpreg;

  tmpreg = I2Cx->STS1;

  tmpreg = I2Cx->STS2;
}

void I2C_ClearSTOPFlag(I2C_Type* I2Cx)
{
  __IO uint32_t tmpreg;

  tmpreg = I2Cx->STS1;

  tmpreg = I2Cx->CTRL1 |= I2C_CTRL1_PEN;
}

void I2C_Delay(uint32_t us)
{
    delay_us(us);
}

I2C_StatusType I2C_Master_Transmit(I2C_Type* I2Cx, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  /* Wait until BUSY flag is reset */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BUSYF, SET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  {
    return I2C_ERROR_STEP_1;
  }

  /* Disable Pos */
  I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

  /* Send START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_STARTF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_2;
  }

  /* Send slave address for write */
  I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Transmit);

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_ADDRF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_3;
  }

  /* Clear ADDR flag */
  I2C_ClearADDRFlag(I2Cx);

  while(Size > 0)
  {
    /* Wait until TDE flag is set */
    if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_TDE, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
    {
      /* Send STOP Condition */
      I2C_GenerateSTOP(I2Cx, ENABLE);

      return I2C_ERROR_STEP_4;
    }

    /* Write data to DR */
    I2C_SendData(I2Cx, (*pData++));
    Size--;
  }

  /* Wait until BTF flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_5;
  }

  /* Send STOP Condition */
  I2C_GenerateSTOP(I2Cx, ENABLE);

  return I2C_OK;
}

I2C_StatusType I2C_Master_Rec(I2C_Type* I2Cx, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  /* Wait until BUSY flag is reset */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BUSYF, SET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  {
    return I2C_ERROR_STEP_1;
  }

  /* Disable Pos */
  I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

  /* Send START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_STARTF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_2;
  }

  /* Send slave address for write */
  I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Receive);

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_ADDRF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_3;
  }

  switch(Size){
  case 1:
      // Disable Acknowledge
      I2C_AcknowledgeConfig(I2Cx, DISABLE);
      // Clear ADDR flag
      I2C_ClearADDRFlag(I2Cx);
      // Send STOP Condition
      I2C_GenerateSTOP(I2Cx, ENABLE);

      if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
      {
          /* Send STOP Condition */
          I2C_GenerateSTOP(I2Cx, ENABLE);

          return I2C_ERROR_STEP_4;
      }
      break;
  case 2:
      /* Enable Pos */
      I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Next);
      /* Disable Acknowledge */
      I2C_AcknowledgeConfig(I2Cx, DISABLE);
      /* Clear ADDR flag */
      I2C_ClearADDRFlag(I2Cx);
      break;
  default:
      /* Enable Acknowledge */
      I2C_AcknowledgeConfig(I2Cx, ENABLE);
      /* Clear ADDR flag */
      I2C_ClearADDRFlag(I2Cx);
      break;
  }
  /* Clear ADDR flag */
//  I2C_ClearADDRFlag(I2Cx);

  while(Size > 0)
  {
      // Wait until TDE flag is set
      if(Size>3)
          if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
          {
              I2C_GenerateSTOP(I2Cx, ENABLE);
              return I2C_ERROR_STEP_4;
          }


      if(Size==3)
      {
          if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
          {
              I2C_GenerateSTOP(I2Cx, ENABLE);
              return I2C_ERROR_STEP_6;
          }
          I2C_AcknowledgeConfig(I2Cx, DISABLE); // Disable Address Acknowledge
      }

      if(Size==2)
      {
          if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
          {
              /* Send STOP Condition */
              I2C_GenerateSTOP(I2Cx, ENABLE);
              return I2C_ERROR_STEP_5;
          }
          /* Send STOP Condition */
          I2C_GenerateSTOP(I2Cx, ENABLE);
      }

      /* Write data to DR */
      (*pData++) = I2C_ReceiveData(I2Cx);
      Size--;

  }

  return I2C_OK;
}

I2C_StatusType I2C_Master_Receive(I2C_Type* I2Cx, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  /* Step 1: Wait until BUSY flag is reset */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BUSYF, SET, I2C_EVT_CHECK_NONE, I2C_TIMEOUT_BUSY_FLAG) != I2C_OK)
  {
    return I2C_ERROR_STEP_1;
  }

  /* Enable PEC value calculation */
  I2C_CalculatePEC(I2Cx, ENABLE);

  /* Disable Pos */
  I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Current);

  /* Enable Acknowledge */
  I2C_AcknowledgeConfig(I2Cx, ENABLE);

  /* Send START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);

  /* Wait until SB flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_STARTF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_2;
  }

  /* Send slave address for write */
  I2C_Send7bitAddress(I2Cx, DevAddress, I2C_Direction_Receive);

  /* Wait until ADDR flag is set */
  if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_ADDRF, RESET, I2C_EVT_CHECK_ACKFAIL, Timeout) != I2C_OK)
  {
    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    return I2C_ERROR_STEP_3;
  }

  if(Size == 1)
  {
    /* Disable Acknowledge */
    I2C_AcknowledgeConfig(I2Cx, DISABLE);

    /* Clear ADDR flag */
    I2C_ClearADDRFlag(I2Cx);

    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);
  }
  else if(Size == 2)
  {
    /* Enable Pos */
    I2C_NACKPositionConfig(I2Cx, I2C_NACKPosition_Next);

    /* Disable Acknowledge */
    I2C_AcknowledgeConfig(I2Cx, DISABLE);

    /* Clear ADDR flag */
    I2C_ClearADDRFlag(I2Cx);
  }
  else
  {
    /* Enable Acknowledge */
    I2C_AcknowledgeConfig(I2Cx, ENABLE);

    /* Clear ADDR flag */
    I2C_ClearADDRFlag(I2Cx);
  }

  while(Size > 0)
  {
    if(Size <= 3)
    {
      /* One byte */
      if(Size == 1)
      {
        /* Wait until RXNE flag is set */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
        {
          /* Send STOP Condition */
          I2C_GenerateSTOP(I2Cx, ENABLE);

          return I2C_ERROR_STEP_4;
        }

        /* Read data from DR */
        (*pData++) = I2C_ReceiveData(I2Cx);
        Size--;
      }
      /* Two bytes */
      else if(Size == 2)
      {
        /* Wait until BTF flag is set */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
        {
          /* Send STOP Condition */
          I2C_GenerateSTOP(I2Cx, ENABLE);

          return I2C_ERROR_STEP_5;
        }

        /* Send STOP Condition */
        I2C_GenerateSTOP(I2Cx, ENABLE);

        /* Read data from DR */
        (*pData++) = I2C_ReceiveData(I2Cx);
        Size--;

        /* Read data from DR */
        (*pData++) = I2C_ReceiveData(I2Cx);
        Size--;
      }
      /* 3 Last bytes */
      else
      {
        /* Wait until BTF flag is set */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
        {
          /* Send STOP Condition */
          I2C_GenerateSTOP(I2Cx, ENABLE);

          return I2C_ERROR_STEP_6;
        }

        /* Disable Acknowledge */
        I2C_AcknowledgeConfig(I2Cx, DISABLE);

        /* Read data from DR */
        (*pData++) = I2C_ReceiveData(I2Cx);
        Size--;

        /* Wait until BTF flag is set */
        if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_BTFF, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
        {
          /* Send STOP Condition */
          I2C_GenerateSTOP(I2Cx, ENABLE);

          return I2C_ERROR_STEP_7;
        }

        /* Send STOP Condition */
        I2C_GenerateSTOP(I2Cx, ENABLE);

        /* Read data from DR */
        (*pData++) = I2C_ReceiveData(I2Cx);
        Size--;

        /* Read data from DR */
        (*pData++) = I2C_ReceiveData(I2Cx);
        Size--;
      }
    }
    else
    {
      /* Wait until RXNE flag is set */
      if(I2C_WaitOnFlagUntilTimeout(I2Cx, I2C_FLAG_RDNE, RESET, I2C_EVT_CHECK_NONE, Timeout) != I2C_OK)
      {
        /* Send STOP Condition */
        I2C_GenerateSTOP(I2Cx, ENABLE);

        return I2C_ERROR_STEP_8;
      }

      /* Read data from DR */
      (*pData++) = I2C_ReceiveData(I2Cx);
      Size--;
    }
  }

  if(I2C_GetFlagStatus(I2Cx, I2C_FLAG_PECERR) == SET)
  {
    I2C_ClearFlag(I2Cx, I2C_FLAG_PECERR);

    return I2C_ERROR_STEP_9;
  }

  return I2C_OK;
}


int IIC_readArray(uint8_t i_device7byteAddress, uint8_t i_dateAddr, uint8_t *o_masterTxBuff, uint32_t i_dateNum)
{
    I2C_StatusType t = I2C_Master_Transmit(I2C_PORT, i_device7byteAddress<<1, &i_dateAddr, 1, 1000);
    if(t!=I2C_OK) return -1;
    t = I2C_Master_Rec(I2C_PORT, i_device7byteAddress<<1, o_masterTxBuff, i_dateNum, 1000);
    if(t!=I2C_OK) return -1;
    return 1;

}


int IIC_sendArray(uint8_t i_device7byteAddress, uint8_t i_dateAddr, const void *i_masterTxBuff, uint32_t i_dateNum)
{
//    uint8_t t_buf[i_dateNum+1]={i_dateAddr};
    uint8_t *t_buf = malloc(i_dateNum+1);
    *t_buf = i_dateAddr;
    memcpy(t_buf+1, i_masterTxBuff, i_dateNum);
    I2C_StatusType t = I2C_Master_Transmit(I2C_PORT, i_device7byteAddress<<1, t_buf, 1+i_dateNum, 1000);
    free(t_buf);
    if(t!=I2C_OK) return -1;
    return 1;
}


int IIC_sendOneByte(uint8_t i_device7byteAddress, uint8_t i_dateAddr, uint8_t i_data)
{
    uint8_t t_buf[2]={i_dateAddr, i_data};
    I2C_StatusType t = I2C_Master_Transmit(I2C_PORT, i_device7byteAddress<<1, t_buf, 2, 1000);
    if(t!=I2C_OK) return -1;
    return 1;
}

uint8_t IIC_ReadOneByte(uint8_t i_IDAddr7bit, uint8_t i_ReadAddr)
{
    I2C_StatusType t = I2C_Master_Transmit(I2C_PORT, i_IDAddr7bit<<1, &i_ReadAddr, 1, 100);
    if(t!=I2C_OK) return 0xff;
    uint8_t t_retValue=0;
    t = I2C_Master_Rec(I2C_PORT, i_IDAddr7bit<<1, &t_retValue, 1, 100);
    if(t!=I2C_OK) return 0xff;
    return t_retValue;
}
